def randCodeSpawn(number):
  '''
  number:字符串长度\n
  随机字符生成函数,返回列表
  '''
  import string,random
  asc_chars = string.ascii_letters
  numbers = string.digits
  rand_list = asc_chars + numbers
  code_list = random.sample(rand_list, number)
  code_str = ''.join(code_list)
  return code_str

def spawnKey(code,timestamp,rand_part):
  '''
  code: 验证码 | timestamp: 生成时间字符串(日期+时刻) | check_code: 随机补位码\n
  密钥复现函数,返回密钥字符串
  '''
  # 预设ascii码串
  check_dec = ""
  for i in code:
    # print(i,ord(i),type(ord(i)))
    asc_code = ord(i)
    # print(asc_code,type(asc_code))
    check_dec += str(asc_code)
  # asc串转化成16进制数，去除进制标识，保留有效部分
  pic_part = hex(int(check_dec))[2:]
  # 提取时间
  min = timestamp.split(' ')[1].split(':')[1]
  sec = timestamp.split(' ')[1].split(':')[2]
  time_num = min + sec
  time_part = hex(int(time_num))[2:]
  return pic_part + time_part + rand_part

def decryptWord(secret_key,word):
  '''
  secret_key: 密钥(字符串) | word: 密文\n
  AES算法解码,返回明文
  '''
  import hashlib,binascii
  from Cryptodome.Cipher import AES
  # 实例化sha256生成器
  sha256_iv = hashlib.sha256()
  # 密钥字符串转换成字节码
  byte_key = secret_key.encode('utf-8')
  # 密钥转换成sha256
  sha256_iv.update(byte_key)
  # 取前16位作为算法偏移量
  iv_str = sha256_iv.hexdigest()[0:16].encode('utf-8')
  # word = user_info['password'].encode('utf-8')
  # 16进制数据转换成2进制码再转换成字符
  password = binascii.unhexlify(word)
  cipher2 = AES.new(byte_key, AES.MODE_CBC, iv=iv_str)
  user_pwd = cipher2.decrypt(password)
  # 取有效值(排除16进制的填充值)
  return user_pwd[0:-user_pwd[-1]]

def checkPicSpawn(cli_id):
  '''
  验证码生成函数
  '''
  import base64
  from captcha.image import ImageCaptcha
  from EnvSetting import checkPicSpawn
  if checkPicSpawn:
    code_str = randCodeSpawn(4)
    image = ImageCaptcha().generate_image(code_str)
    path = 'static/{}.jpg'.format(cli_id)
    image.save(path)
  else:
    path,code_str = 'static/7c5y.jpg','7c5y'
  img_stream = ''
  with open(path, 'rb') as img_f:
    img_stream = img_f.read()
    img_stream = base64.b64encode(img_stream)
  return [img_stream,code_str]

def spawnSalt():
  '''
  生成16位16进制盐值
  '''
  import random
  hex_str = ''
  for i in range(16):
    hex_str += hex(random.randint(0,15))[2:]
  return hex_str

def idVerification(user_info):
  '''
  user_info: JSON数据(需要包含clientid,account,timestamp,checkcode,password键)\n
  提取数据,验证身份兼解码,返回字节码明文
  '''
  import sqlLink,EnvSetting
  client_id = user_info['clientid']
  # 提取身份验证码
  sql = "select client_id,code from `{}`.`{}` where client_id='{}'".format(EnvSetting.user_db,EnvSetting.client_tab,client_id)
  result = sqlLink.sqlGetData(EnvSetting.user_db,sql)
  if result == '输入格式有误' or len(result)==0:
    return '临时身份已过期'
  try:
    pic_code = result[0][1]
    account,timestamp,rand_part,word = user_info['account'],user_info['timestamp'],user_info['checkcode'],user_info['password']
    # 复现密钥
    secret_key = spawnKey(code=pic_code,timestamp=timestamp,rand_part=rand_part)
    pwd = decryptWord(secret_key=secret_key,word=word)
    print(secret_key,'密钥',pic_code,'验证码',account,'账号\n',pwd,'--明文--')
    return pwd
  except ValueError:
    print('验证码错误') # 解码出错,验证码不匹配
    return '验证码错误'
  # except:
  #   return flask.redirect(source_path + '?res=填写信息有误')

def sha256Encode(code: str):
  '''
  SHA256算法加密字符串
  '''
  import hashlib
  hash = hashlib.new("sha256")
  hash.update(code.encode('utf-8'))
  return hash.hexdigest()

def hs256Encode(salt: str,password: bytes):
  '''
  salt:盐值(字符串) | password:明文(字节码)\n
  hmac-sha256散列算法，在SHA系列基础上添加盐值干扰
  '''
  import hmac
  hash = hmac.new(key=salt.encode('utf-8'),msg=password,digestmod="sha256")
  return hash.hexdigest()

def loginTokenSave(account,clientId):
  '''
  account:用户名 | clientId:客户端临时ID\n
  存储简易自动登录token令牌
  '''
  import time,sqlLink,EnvSetting
  import model.timeSolute
  token = sha256Encode(account + str(time.time()).replace('.',''))
  start_end_Times = model.timeSolute.deadlineComputer(EnvSetting.valid_autoLogin,EnvSetting.valid_loginClass)
  startTime,endTime = start_end_Times['startTime'],start_end_Times['endTime']
  sql = f"select `account` from `{EnvSetting.user_db}`.`{EnvSetting.loginToken_tab}` WHERE `account`='{account}'"  
  res = sqlLink.sqlGetData(EnvSetting.user_db,sql)
  if res == '输入格式有误':
    print('token生成失败:用户名称格式有误')
    return '输入值不合法'
  elif res:
    # 更新token
    sql = f"UPDATE `{EnvSetting.user_db}`.`{EnvSetting.loginToken_tab}` SET `token`='{token}',`starttime`='{startTime}',`endtime`='{endTime}',`clientId`='{clientId}' WHERE account='{account}';"
    res = sqlLink.UpdateData(EnvSetting.user_db,sql)
    return {'token':token,'endtime':endTime} if res == 'update OK' else '自动登录令牌更新失败'
  else:
    # 新建token
    sql = f"INSERT INTO `{EnvSetting.user_db}`.`{EnvSetting.loginToken_tab}` (`account`,`token`,`starttime`,`endtime`,`clientid`) VALUES ('{account}','{token}','{startTime}','{endTime}','{clientId}');"
    res = sqlLink.InsertData(EnvSetting.user_db,sql)
    return {'token':token,'endtime':endTime} if res=='insert OK' else '自动登录令牌保存失败'

def endTimeCheck(start_time,end_time):
  '''
  user_time:用户\n
  时间有效性检测
  '''
  import time
  local_time = time.localtime()
  if start_time == None:
    start_time = time.time() - 100
  else:
    start_time = time.strptime(start_time,"%Y-%m-%d %H:%M:%S")
  end_time = time.strptime(end_time,"%Y-%m-%d %H:%M:%S")
  if end_time >= local_time and start_time <= local_time :
    return True
  else:
    return False

def userTokenSave(account,clientId):
  '''
  account:用户名 | clientId:客户端临时ID\n
  存储简易用户令牌
  '''
  import time,sqlLink,EnvSetting,model.timeSolute
  user_token = sha256Encode(account+clientId+str(time.time()).replace('.',''))
  start_end_Times = model.timeSolute.deadlineComputer(EnvSetting.valid_userState,EnvSetting.valid_userStateClass)
  startTime,endTime = start_end_Times['startTime'],start_end_Times['endTime']
  sql = f"select `account` from `{EnvSetting.user_db}`.`{EnvSetting.userToken_tab}` WHERE `account`='{account}'"  
  res = sqlLink.sqlGetData(EnvSetting.user_db,sql)
  if res == '输入格式有误':
    print('token生成失败:用户名称格式有误')
    return '输入值不合法'
  elif res:
    # 更新token
    sql = f"UPDATE `{EnvSetting.user_db}`.`{EnvSetting.userToken_tab}` SET `token`='{user_token}',`starttime`='{startTime}',`endtime`='{endTime}',`clientId`='{clientId}' WHERE account='{account}';"
    res = sqlLink.UpdateData(EnvSetting.user_db,sql)
    return [user_token] if res == 'update OK' else '用户令牌更新失败'
  else:
    # 新建token
    sql = f"INSERT INTO `{EnvSetting.user_db}`.`{EnvSetting.userToken_tab}` (`account`,`token`,`starttime`,`endtime`,`clientid`) VALUES ('{account}','{user_token}','{startTime}','{endTime}','{clientId}');"
    res = sqlLink.InsertData(EnvSetting.user_db,sql)
    return [user_token] if res == 'insert OK' else '用户令牌保存失败'

def idVerifyForChangePwd(user_info):
  '''
  user_info: JSON数据(字典类型)\n
  身份验证兼解码函数,返回原密码和新密码字节码明文
  '''
  import sqlLink,EnvSetting,userSign
  client_id = user_info['clientid']
  # 提取身份验证码
  sql = "select client_id,code from `{}`.`{}` where client_id='{}'".format(EnvSetting.user_db,EnvSetting.client_tab,client_id)
  result = sqlLink.sqlGetData(EnvSetting.user_db,sql)
  if result == '输入格式有误' or len(result)==0:
    return '临时身份已过期'
  try:
    pic_code = result[0][1]
    account,timestamp,rand_part,last_pwd,new_pwd = user_info['account'],user_info['timestamp'],user_info['checkcode'],user_info['password']['last'],user_info['password']['new']
    # 复现密钥
    secret_key = userSign.spawnKey(code=pic_code,timestamp=timestamp,rand_part=rand_part)
    last_word = userSign.decryptWord(secret_key=secret_key,word=last_pwd)
    new_word = userSign.decryptWord(secret_key=secret_key,word=new_pwd)
    print('密钥',secret_key,'\n明文',last_word,'\nnew',new_word)
    return [last_word,new_word]
  except ValueError:
    print('验证码错误') # 解码出错,验证码不匹配
    return '验证码错误'

def userTokenCheck(account,user_token):
  '''
  account:账户 | user_token:token令牌\n
  身份令牌验证
  '''
  import sqlLink,EnvSetting
  sql = f"SELECT account,token,starttime,endtime FROM `{EnvSetting.user_db}`.`{EnvSetting.userToken_tab}` where `account`='{account}'"
  res = sqlLink.sqlGetData(EnvSetting.user_db,sql)
  if type(res) == str:
    return res
  token,start_time,end_time = res[0][1],str(res[0][2]),str(res[0][3])
  time_valid = endTimeCheck(start_time,end_time)
  if user_token != token or not time_valid:
    return 'token已过期'
  return True